﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Beat_em_Up
{
    /// <summary>
    /// Classe responsável pela criação de objetos 3d
    /// </summary>
    class Object3D
    {

        protected Model model;
        protected Vector3 position;
        protected float rotx, roty, rotz, scale;
        protected BoundingBox hitBox;
        protected Matrix world;

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        public Object3D(Model model)
        {
            this.model = model;
            this.position = Vector3.Zero;
            this.scale = 1f;
            this.rotx = roty = rotz = 0f;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <param name="position"></param>
        /// <param name="scale"></param>
        public Object3D(Model model, Vector3 position, float scale)
        {
            this.model = model;
            this.position = position;
            this.scale = scale;
            this.rotx = roty = rotz = 0f;
        }

        /// <summary>
        /// Função responsável pela atualização dos dados de escala, rotação e translação da nave 3D
        /// </summary>
        public void Update(GameTime gameTime)
        {
            world = Matrix.CreateScale(scale)
               * Matrix.CreateRotationX(rotx)
               * Matrix.CreateRotationY(roty)
               * Matrix.CreateRotationZ(rotz)
               * Matrix.CreateTranslation(position);
        }

        /// <summary>
        /// Desenha o modelo da nave 3D
        /// </summary>
        /// <param name="model"></param>
        /// <param name="world"></param>
        /// <param name="view"></param>
        /// <param name="projection"></param>
 
        private void DrawModel(Model model, Matrix world, Matrix view, Matrix projection)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;
                    effect.EnableDefaultLighting();
                  }
                mesh.Draw();
            }
        }

        /// <summary>
        /// Atualiza a caixa de colisão do objeto
        /// </summary>
        /// <param name="model">Recebe o modelo do objeto</param>
        /// <param name="worldTransform">Recebe a matriz de transformação de mundo do objeto</param>
        /// <returns>Uma caixa de colisão com o tamanho certo do objeto</returns>
        protected BoundingBox UpdateBoundingBox(Model model, Matrix worldTransform)
        {
            //Inicia os cantos da caixa com valores maximo e minimo
            Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
            Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);

            //para cada malha e submalha no modelo
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;//o número de bytes de um vértice para o próximo
                    int vertexBufferSize = meshPart.NumVertices * vertexStride;//o tamanho do buffer (array) de vértices
                    float[] vertexData = new float[vertexBufferSize / sizeof(float)];//um array [] com o tamanho certo para caber o vertexBufferSize
                    meshPart.VertexBuffer.GetData<float>(vertexData);//pegar GetData a informação de float e preencher o buffer de vértices
                    //percorrer de i = 0 até o tamanho do buffer de vértices em float andando de float em float (ver cada ponto)
                    for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
                    {
                        //cria um vertor transformado com os tres pontos em relação ao mundo
                        Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);

                        //calcula os pontos, o mínimo e o máximo
                        min = Vector3.Min(min, transformedPosition);
                        max = Vector3.Max(max, transformedPosition);
                    }
                }
            }

            //cria e retorna o BoundingBox segundo os valores calculados para o tamanho
            return new BoundingBox(min, max);
        }


        public void Draw(Camera camera)
        {
            DrawModel(model, world, camera.View, camera.Projection);
        }
    }
}
